/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla Skywriter.
 *
 * The Initial Developer of the Original Code is
 * Mozilla.
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *      Kevin Dangoor (kdangoor@mozilla.com)
 *      Julian Viereck <julian DOT viereck AT gmail DOT com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */


define(function(require, exports, module) {
"use strict";

require("ace/lib/fixoldbrowsers");
require("ace/config").init();
var env = {};

var dom = require("ace/lib/dom");
var event = require("ace/lib/event");
var theme = require("ace/theme/textmate");
var EditSession = require("ace/edit_session").EditSession;
var UndoManager = require("ace/undomanager").UndoManager;

var vim = require("ace/keyboard/vim").handler;
var emacs = require("ace/keyboard/emacs").handler;
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;

var Renderer = require("ace/virtual_renderer").VirtualRenderer;
var Editor = require("ace/editor").Editor;
var MultiSelect = require("ace/multi_select").MultiSelect;

// workers do not work for file:
if (location.protocol == "file:")
    EditSession.prototype.$useWorker = false;
/************** modes ***********************/
var modes = [];
function getModeFromPath(path) {
    var mode = modesByName.text;
    for (var i = 0; i < modes.length; i++) {
        if (modes[i].supportsFile(path)) {
            mode = modes[i];
            break;
        }
    }
    return mode;
};

var Mode = function(name, desc, extensions) {
    this.name = name;
    this.desc = desc;
    this.mode = "ace/mode/" + name;
    this.extRe = new RegExp("^.*\\.(" + extensions + ")$", "g");
};

Mode.prototype.supportsFile = function(filename) {
    return filename.match(this.extRe);
};

var modesByName = {
    c9search:   ["C9Search"     , "c9search_results"],
    coffee:     ["CoffeeScript" , "coffee|^Cakefile"],
    coldfusion: ["ColdFusion"   , "cfm"],
    csharp:     ["C#"           , "cs"],
    css:        ["CSS"          , "css"],
    diff:       ["Diff"         , "diff|patch"],
    golang:     ["Go"           , "go"],
    groovy:     ["Groovy"       , "groovy"],
    haxe:       ["haXe"         , "hx"],
    html:       ["HTML"         , "htm|xhtml"], // html move to php,html template file has php tag.
    c_cpp:      ["C/C++"        , "c|cc|cpp|cxx|h|hh|hpp"],
    clojure:    ["Clojure"      , "clj"],
    java:       ["Java"         , "java"],
    javascript: ["JavaScript"   , "js"],
    json:       ["JSON"         , "json"],
    jsx:        ["JSX"          , "jsx"],
    latex:      ["LaTeX"        , "latex|tex|ltx|bib"],
    less:       ["LESS"         , "less"],
    liquid:     ["Liquid"       , "liquid"],
    lua:        ["Lua"          , "lua"],
    luapage:    ["LuaPage"      , "lp"], // http://keplerproject.github.com/cgilua/manual.html#templates
    markdown:   ["Markdown"     , "md|markdown"],
    ocaml:      ["OCaml"        , "ml|mli"],
    perl:       ["Perl"         , "pl|pm"],
    pgsql:      ["pgSQL"        , "pgsql"],
    php:        ["PHP"          , "php|phtml|html"],
    powershell: ["Powershell"   , "ps1"],
    python:     ["Python"       , "py"],
    ruby:       ["Ruby"         , "ru|gemspec|rake|rb"],
    scad:       ["OpenSCAD"     , "scad"],
    scala:      ["Scala"        , "scala"],
    scss:       ["SCSS"         , "scss|sass"],
    sh:         ["SH"           , "sh|bash|bat"],
    sql:        ["SQL"          , "sql"],
    svg:        ["SVG"          , "svg"],
    tcl:        ["Tcl"          , "tcl"],
    text:       ["Text"         , "txt"],
    textile:    ["Textile"      , "textile"],
    xml:        ["XML"          , "xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl"],
    xquery:     ["XQuery"       , "xq"],
    yaml:       ["YAML"         , "yaml"]
};
for (var name in modesByName) {
    var mode = modesByName[name];
    mode = new Mode(name, mode[0], mode[1])
    modesByName[name] = mode;
    modes.push(mode);
}
/*********** demo documents ***************************/

function initDoc(file, path, doc) {
    if (doc.prepare)
        file = doc.prepare(file);

    var session = new EditSession(file);
    session.setUndoManager(new UndoManager());
    doc.session = session;
    doc.path = path;
    if (doc.wrapped) {
    	session.setTabSize(4);
    	session.setUseSoftTabs(true);
        session.setUseWrapMode(true);
        session.setFoldStyle("markbegin");
    	env.editor.setShowFoldWidgets(true);
    	env.editor.setHighlightActiveLine(true);
        session.setWrapLimitRange(null, null);
    }
    var mode = getModeFromPath(path)
    session.modeName = mode.name;
    session.setMode(mode.mode);
}
if (window.require && window.require.s) try {
    for (var path in window.require.s.contexts._.loaded) {
        if (path.indexOf("!") != -1)
            path = path.split("!").pop();
        else
            path = path + ".js";
        ownSource[path] = ""
    }
} catch(e) {}

/*********** create editor ***************************/
var container = document.getElementById("editor");

// Splitting.
var Split = require("ace/split").Split;
var split = new Split(container, theme, 1);
env.editor = split.getEditor(0);
split.on("focus", function(editor) {
    env.editor = editor;
});
env.split = split;
window.env = env;
window.ace = env.editor;
env.editor.setAnimatedScroll(true);

var consoleEl = dom.createElement("div");
container.appendChild(consoleEl);
consoleEl.style.position="fixed"
consoleEl.style.bottom = "1px"
consoleEl.style.right = 0
consoleEl.style.background = "white"
consoleEl.style.border = "1px solid #baf"
consoleEl.style.zIndex = "100"
var cmdLine = new singleLineEditor(consoleEl);
cmdLine.editor = env.editor;
env.editor.cmdLine = cmdLine;

env.editor.commands.addCommands([{
    name: "gotoline",
    bindKey: {win: "Ctrl-L", mac: "Command-L"},
    exec: function(editor, line) {
        if (typeof line == "object") {
            var arg = this.name + " " + editor.getCursorPosition().row;
            editor.cmdLine.setValue(arg, 1)
            editor.cmdLine.focus()
            return
        }
        line = parseInt(line, 10);
        if (!isNaN(line))
            editor.gotoLine(line);
    },
    readOnly: true
}, {
    name: "find",
    bindKey: {win: "Ctrl-F", mac: "Command-F"},
    exec: function(editor, needle) {
        if (typeof needle == "object") {
            var arg = this.name + " " + editor.getCopyText()
            editor.cmdLine.setValue(arg, 1)
            editor.cmdLine.focus()
            return
        }
        editor.find(needle);
    },
    readOnly: true
}, {
    name: "focusCommandLine",
    bindKey: "shift-esc",
    exec: function(editor, needle) { editor.cmdLine.focus(); },
    readOnly: true
}])

cmdLine.commands.bindKeys({
    "Shift-Return|Ctrl-Return|Alt-Return": function(cmdLine) { cmdLine.insert("\n")},
    "Esc|Shift-Esc": function(cmdLine){ cmdLine.editor.focus(); },
    "Return": function(cmdLine){
        var command = cmdLine.getValue().split(/\s+/);
        var editor = cmdLine.editor;
        editor.commands.exec(command[0], editor, command[1]);
        editor.focus();
    },
})

cmdLine.commands.removeCommands(["find", "gotoline", "findall", "replace", "replaceall"])

/**
 * This demonstrates how you can define commands and bind shortcuts to them.
 */

var commands = env.editor.commands;
commands.addCommand({
    name: "save",
    bindKey: {win: "Ctrl-S", mac: "Command-S"},
    exec: function() {alert("Fake Save File");}
});

var keybindings = {
    // Null = use "default" keymapping
    ace: null,
    vim: vim,
    emacs: emacs,
    // This is a way to define simple keyboard remappings
    custom: new HashHandler({
        "gotoright":      "Tab",
        "indent":         "]",
        "outdent":        "[",
        "gotolinestart":  "^",
        "gotolineend":    "$"
     })
};



/*********** manage layout ***************************/
var consoleHight = 20;

$(window).resize(function(){	
	var left = env.split.$container.offsetLeft;
    var width = $(window).width()-$('.ui-layout-west').width()-left-10;    
    container.style.width = width + "px";
    container.style.height = document.documentElement.clientHeight - consoleHight-110 + "px";
    env.split.resize();

    consoleEl.style.width = width + "px";
    cmdLine.resize()
}).trigger('resize');

bindDropdown("soft_wrap", function(value) {
    var session = env.editor.getSession();
    var renderer = env.editor.renderer;
    switch (value) {
        case "off":
            session.setUseWrapMode(false);
            renderer.setPrintMarginColumn(80);
            break;
        case "120":
            session.setUseWrapMode(true);
            session.setWrapLimitRange(120, 120);
            renderer.setPrintMarginColumn(120);
            break;
        case "80":
            session.setUseWrapMode(true);
            session.setWrapLimitRange(80, 80);
            renderer.setPrintMarginColumn(80);
            break;
        case "free":
            session.setUseWrapMode(true);
            session.setWrapLimitRange(null, null);
            renderer.setPrintMarginColumn(80);
            break;
    }
});
bindDropdown("fontsize", function(value) {
    env.split.setFontSize(value);
});
bindDropdown("folding", function(value) {
    env.editor.getSession().setFoldStyle(value);
    env.editor.setShowFoldWidgets(value !== "manual");
});
bindDropdown("keybinding", function(value) {
    env.editor.setKeyboardHandler(keybindings[value]);
});
function bindDropdown(id, callback) {
    var el = document.getElementById(id);
    if (localStorage && localStorage.getItem(id))
        el.value = localStorage.getItem(id);

    var onChange = function() {
        callback(el.value);
        //saveOption(el);
    };

    el.onchange = onChange;
    onChange();
}
/************** dragover ***************************/
event.addListener(container, "dragover", function(e) {
    return event.preventDefault(e);
});

event.addListener(container, "drop", function(e) {
    var file;
    try {
        file = e.dataTransfer.files[0];
        if (window.FileReader) {
            var reader = new FileReader();
            reader.onload = function() {
                var mode = getModeFromPath(file.name);

                env.editor.session.doc.setValue(reader.result);
                modeEl.value = mode.name;
                env.editor.session.setMode(mode.mode);
                env.editor.session.modeName = mode.name;
            };
            reader.readAsText(file);
        }
        return event.preventDefault(e);
    } catch(err) {
        return event.stopEvent(e);
    }
});

// add multiple cursor support to editor
require("ace/multi_select").MultiSelect(env.editor);



function singleLineEditor(el) {
    var renderer = new Renderer(el);
    renderer.scrollBar.element.style.display = "none";
    renderer.scrollBar.width = 0;
    renderer.content.style.height = "auto";

    renderer.screenToTextCoordinates = function(x, y) {
        var pos = this.pixelToScreenCoordinates(x, y);
        return this.session.screenToDocumentPosition(
            Math.min(this.session.getScreenLength() - 1, Math.max(pos.row, 0)),
            Math.max(pos.column, 0)
        );
    };
    // todo size change event
    renderer.$computeLayerConfig = function() {
        var longestLine = this.$getLongestLine();
        var firstRow = 0;
        var lastRow = this.session.getLength();
        var height = this.session.getScreenLength() * this.lineHeight;

        this.scrollTop = 0;
        var config = this.layerConfig;
        config.width = longestLine;
        config.padding = this.$padding;
        config.firstRow = 0;
        config.firstRowScreen = 0;
        config.lastRow = lastRow;
        config.lineHeight = this.lineHeight;
        config.characterWidth = this.characterWidth;
        config.minHeight = height;
        config.maxHeight = height;
        config.offset = 0;
        config.height = height;

        this.$gutterLayer.element.style.marginTop = 0 + "px";
        this.content.style.marginTop = 0 + "px";
        this.content.style.width = longestLine + 2 * this.$padding + "px";
        this.content.style.height = height + "px";
        this.scroller.style.height = height + "px";
        this.container.style.height = height + "px";
    };
    renderer.isScrollableBy=function(){return false};

    var editor = new Editor(renderer);
    new MultiSelect(editor);
    editor.session.setUndoManager(new UndoManager());

    editor.setHighlightActiveLine(false);
    editor.setShowPrintMargin(false);
    editor.renderer.setShowGutter(false);
    // editor.renderer.setHighlightGutterLine(false);
    return editor;
};

env.editor.setTheme("ace/theme/dreamweaver");
env.editor.session.setMode("ace/mode/javascript");
var EditSession = require("ace/edit_session").EditSession
//var js = new EditSession("some js code")
//env.editor.setSession(js)
var session = env.editor.getSession();
session.setTabSize(4);
session.setUseSoftTabs(true);
document.getElementById('editor').style.fontSize='12px';
//默认自动换行    
session.setUseWrapMode(true);

//env.editor.setReadOnly(true); // 设置只读模式


function setSession(session) {
    var session = env.split.setSession(session);
    env.editor.focus();
}

    
//    maintab.bind( "tabsselect", function(event, ui) {
//  alert($(ui.tab).html());
//});

$('#editor').hide();    
maintab.bind( "tabsselect", function(event, ui) {
	var st = $(ui.tab).attr('href');
	var name = st.replace(/#maintab_/g,'');	
	var doc = fileCache[name];
	if (doc && doc.session){
		setSession(doc.session);
		$('#editor').appendTo(st).show();
	}
});

//--- Implement Cut/Copy/Paste --------------------------------------------
var clipboardNode = null; /* 剪切板的借点 */
var pasteMode = null; // 操作动作，cut,copy,paste

function copyPaste(action, node) {
  switch( action ) {
  case "cut":
  case "copy":
    clipboardNode = node;
    pasteMode = action;
    break;
  case "paste":
    if( !clipboardNode ) {
      alert("Clipoard is empty.");
      break;
    }
    if( pasteMode == "cut" ) {
      // Cut mode: check for recursion and remove source
      var isRecursive = false;
      var cb = clipboardNode.toDict(true, function(dict){
        // If one of the source nodes is the target, we must not move
        if( dict.key == node.data.key )
          isRecursive = true;
      });
      if( isRecursive ) {
        alert("Cannot move a node to a sub node.");
        return;
      }
      node.addChild(cb);
      clipboardNode.remove();
    } else {
      // Copy mode: prevent duplicate keys:
      var cb = clipboardNode.toDict(true, function(dict){
        dict.title = "Copy of " + dict.title;
        delete dict.key; // Remove key, so a new one will be created
      });
      node.addChild(cb);
    }
    clipboardNode = pasteMode = null;
    break;
  default:
    alert("Unhandled clipboard action '" + action + "'");
  }
};

// --- Contextmenu helper --------------------------------------------------
function bindContextMenu(span) {
  // Add context menu to this node:
  $(span).contextMenu({menu: "myMenu"}, function(action, el, pos) {
    // The event was bound to the <span> tag, but the node object
    // is stored in the parent <li> tag
    var node = $.ui.dynatree.getNode(el);
    switch( action ) {
    case "cut":
    case "copy":
    case "paste":
      copyPaste(action, node);
      break;
    case "edit":
        $(el).trigger('click'); // trigger click event.
        break;
    default:
      alert("Todo: appply action '" + action + "' to node " + node);
    }
  });
};


	jQuery(function(){
		jQuery("#leftmenucontent").dynatree({
			title: "Lazy loading sample",
			//fx: { height: "toggle", duration: 200 },
			autoFocus: false, // Set focus to first child, when expanding or lazy-loading.
			// In real life we would call a URL on the server like this:
          initAjax: {
              url: ADMIN_BASEURL+"/admin/ace/editors/listfolder.json",
              data: { mode: "funnyMode" }
          },
          onCreate: function(node, span){
              bindContextMenu(span);
            },

			onActivate: function(node) {
				jQuery("#echoActive").text("" + node + " (" + node.getKeyPath()+ ")");
			},
			onClick: function(node, event) {
				if( $(".contextMenu:visible").length > 0 ){
			          $(".contextMenu").hide();
			    }

				if( node.data.isFolder ){
					return;
				}
				$('#editor').hide();
				
				var name = node.data.key.replace(/\\/g,'_').replace(/\/|\.|:/g,'_');
				var doc = fileCache[name];
				if(typeof doc == "undefined"){
					doc = {};
					fileCache[name] = doc;
				}
				doc.name = name;
				doc.wrapped = true;
				
				var tab_title = node.data.title;
				$("#leftmenucontent li").find('.ui-state-highlight').removeClass('ui-state-highlight');
				$(this).addClass('ui-state-highlight');
				
				var st = "#maintab_"+name;
				if($(st).html() != null ) {					
					maintab.tabs('select',st);
				} else {
					maintab.tabs('add',st,tab_title);
					$(st).css('position','relative');
					$('#editor').appendTo(st);
					var path = ADMIN_BASEURL+"/admin/ace/editors/loadfile?file="+node.data.key;
					// x,为ajax返回的文件内容  var net = require("ace/lib/net");
					//net.get(path, function(x) {
					//        initDoc(x, path, doc);
					//        setSession(doc.session)
					//});
					
					setSession(new EditSession("loading content..."));
					$('#editor').show();
					$.ajax({
						url: path,
						type: "GET",
						dataType: "html",
						complete : function (req, err) {
							initDoc(req.responseText, path, doc);
					        setSession(doc.session);
					        doc.session.getDocument().on('change',function(){
					        	var selected = maintab.tabs( "option", "selected" );
					        	var cur_tab = $('.ui-tabs-nav li',maintab).eq(selected);
					        	if(cur_tab.find('.ui-ace-save').size()==0){
					        		cur_tab.find('a').after('<span class="ui-ace-save ui-icon ui-icon-disk" title="Save File"></span>');
					        		cur_tab.find('.ui-ace-save').click(function(){
					        			alert('save file '+node.data.key);
					        			alert(doc.session);
					        		});
					        	}
					        	//alert('change content');
					        });
					        
						}
					});
					
				}
			},

			onLazyRead: function(node){
				// In real life we would call something like this:
            	node.appendAjax({
            	    url: ADMIN_BASEURL+"/admin/ace/editors/listfolder.json",
		            data: {key: node.data.key,
            		       mode: "funnyMode"
                    }
              });
			}
		});
	});
});
